home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 8 / The Arsenal Files Collection #8 (Arsenal Computer) (1996).ISO / prg_casm / recio215.zip / RGET.C < prev    next >
C/C++ Source or Header  |  1996-10-26  |  25KB  |  682 lines

  1. /*****************************************************************************
  2.    MODULE: rget.c
  3.   PURPOSE: recio input functions
  4. COPYRIGHT: (C) 1994-1996, William Pierpoint
  5.  COMPILER: Borland C Version 3.1
  6.        OS: MSDOS Version 6.2
  7.   VERSION: 2.15
  8.   RELEASE: October 26, 1996
  9. *****************************************************************************/
  10.  
  11. #include <ctype.h>
  12. #include <errno.h>
  13. #include <limits.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17.  
  18. #include "recio.h"
  19.  
  20. extern int _risready(REC *rp, int mode);
  21. extern int _rmoderror(REC *rp, int mode);
  22. extern void _rsetexitfn(REC *rp);
  23.  
  24. /* private macros */
  25. #define RECBUFSIZ_MIN 2         /* min one character and new line */
  26. #define FLDBUFSIZ_MIN 1         /* min one character */
  27.  
  28. #define RECBUFSIZE    max(RECBUFSIZ, RECBUFSIZ_MIN)
  29. #define FLDBUFSIZE    max(FLDBUFSIZ, FLDBUFSIZ_MIN)
  30.  
  31. #define rcol(rp)         ((rp)->r_colno)
  32. #define rflags(rp)       ((rp)->r_flags)
  33. #define rfd(rp)          ((rp)->r_fd)
  34. #define rfp(rp)          ((rp)->r_fp)
  35. #define rreclen(rp)      ((rp)->r_reclen)
  36. #define rrecsiz(rp)      ((rp)->r_recsiz)
  37. #define rfldsiz(rp)      ((rp)->r_fldsiz)
  38. #define rfldch(rp)       ((rp)->r_fldch)
  39. #define rtxtch(rp)       ((rp)->r_txtch)
  40.  
  41. /****************************************************************************/
  42. static int                   /* return error number (0=no error)            */
  43.     _rsetfldsiz(             /* set field buffer size                       */
  44.         REC   *rp,           /* record pointer                              */
  45.         size_t fldsiz)       /* field buffer size                           */
  46. /****************************************************************************/
  47. {
  48.     int errnum=0;            /* error number */
  49.     char *fldp;              /* pointer to new field buffer */
  50.  
  51.     /* if no memory allocated to field buffer */
  52.     if (!rflds(rp)) {
  53.  
  54.         /* determine minimum size of field buffer */
  55.         fldsiz = max(fldsiz, FLDBUFSIZE);
  56.  
  57.         /* initially allocate memory for field buffer */
  58.         do {
  59.             fldp = (char *) calloc(fldsiz+1, sizeof(char));
  60.             if (!fldp) {
  61.                 errnum = rseterr(rp, R_ENOMEM);
  62.                 if (errnum) goto done;
  63.             }
  64.         } while (!fldp);
  65.         rflds(rp) = fldp;
  66.         rfldsiz(rp) = fldsiz;
  67.         _rsetexitfn(rp);
  68.  
  69.     /* if field buffer needs to be larger */
  70.     } else if (fldsiz > rfldsiz(rp)) {
  71.  
  72.         /* reallocate memory for field buffer */
  73.         do {
  74.             fldp = (char *) realloc(rflds(rp), fldsiz+1);
  75.             if (!fldp) {
  76.                 errnum = rseterr(rp, R_ENOMEM);
  77.                 if (errnum) goto done;
  78.             }
  79.         } while (!fldp);
  80.         rflds(rp) = fldp;
  81.         rfldsiz(rp) = fldsiz;
  82.     }
  83.  
  84. done:
  85.     return errnum;
  86. }
  87.  
  88. /****************************************************************************/
  89. static int                   /* return error number (0=no error)            */
  90.     _rsetrecsiz(             /* set record buffer size                      */
  91.         REC   *rp,           /* record pointer                              */
  92.         size_t recsiz)       /* record buffer size                          */
  93. /****************************************************************************/
  94. {
  95.     int errnum=0;            /* error number */
  96.     char *recp;              /* pointer to new record buffer */
  97.  
  98.     /* if no memory allocated to field buffer */
  99.     if (!rrecs(rp)) {
  100.  
  101.         /* determine minimum size of record buffer */
  102.         recsiz = max(recsiz, RECBUFSIZE);
  103.  
  104.         /* initially allocate memory for record buffer */
  105.         do {
  106.             recp = (char *) calloc(recsiz+1, sizeof(char));
  107.             if (!recp) {
  108.                 errnum = rseterr(rp, R_ENOMEM);
  109.                 if (errnum) goto done;
  110.             }
  111.         } while (!recp);
  112.         rrecs(rp) = recp;
  113.         rrecsiz(rp) = recsiz;
  114.         _rsetexitfn(rp);
  115.  
  116.     /* if record buffer needs to be larger */
  117.     } else if (recsiz > rrecsiz(rp)) {
  118.  
  119.         /* reallocate memory for record buffer */
  120.         do {
  121.             recp = (char *) realloc(rrecs(rp), recsiz+1);
  122.             if (!recp) {
  123.                 errnum = rseterr(rp, R_ENOMEM);
  124.                 if (errnum) goto done;
  125.             }
  126.         } while (!recp);
  127.         rrecs(rp) = recp;
  128.         rrecsiz(rp) = recsiz;
  129.     }
  130.  
  131. done:
  132.     return errnum;
  133. }
  134.  
  135. /****************************************************************************/
  136. static int                   /* return !0 on match                          */
  137.     risfldch(                /* is character the field separator character? */
  138.         REC *rp,             /* record pointer                              */
  139.         int  ch)             /* character to test                           */
  140. /****************************************************************************/
  141. {
  142.     int ismatch=0;           /* return 0 if no match */
  143.  
  144.     if (isascii(ch)) { 
  145.         if (rfldch(rp) == ' ') {
  146.             ismatch = isspace(ch);
  147.         } else {
  148.             ismatch = (!(ch - rfldch(rp)));
  149.         }
  150.     }
  151.     return ismatch;
  152. }
  153.  
  154. /****************************************************************************/
  155. static int                   /* return !0 on match                          */
  156.     ristxtch(                /* is character the text delimiter character?  */
  157.         REC *rp,             /* record pointer                              */
  158.         int  ch)             /* character to test                           */
  159. /****************************************************************************/
  160. {
  161.     int ismatch=0;           /* return 0 if no match */
  162.  
  163.     if (isascii(ch)) { 
  164.         if (rtxtch(rp) == ' ') {
  165.             ismatch = isspace(ch);
  166.         } else {
  167.             ismatch = (!(ch - rtxtch(rp)));
  168.         }
  169.     }
  170.     return ismatch;
  171. }
  172.  
  173. /****************************************************************************/
  174. static size_t                /* return length of field                      */
  175.     _rfldlen(                /* get length of field                         */
  176.         REC *rp)             /* record pointer                              */
  177. /****************************************************************************/
  178. {
  179.     size_t len=0;            /* length of field (0=missing field) */
  180.     size_t col;              /* column location */
  181.     size_t co;               /* temporary column location */
  182.     int qstate=0;            /* nested quoted text state (0=off; 1=on) */
  183.  
  184.     /* clear quoted text char flag */
  185.     rflags(rp) &= ~_R_TXT;
  186.  
  187.     /* skip past any leading whitespace */
  188.     for (col=rcol(rp); col < rreclen(rp); col++) {
  189.         if (!isspace(rrecs(rp)[col])) break;
  190.     }
  191.  
  192.     /* is first non-whitespace character the txtch? */
  193.     if (ristxtch(rp, rrecs(rp)[col])) { /* yes, quoted text */
  194.         rflags(rp) |= _R_TXT;           /* set quoted text char flag  */
  195.         if (risfldch(rp, ' ')) {        /* fldch == ' '; txtch != ' ' */
  196.             /* skip over characters that are not txtch */
  197.             for (col++; col < rreclen(rp); col++) {
  198.                 /* if txtch, see if end-of-field */
  199.                 if (ristxtch(rp, rrecs(rp)[col])) {
  200.                     /* don't end field within any nested quoted text */
  201.                     if (ristxtch(rp, '"')) {
  202.                         qstate = !qstate;
  203.                         if (!qstate) continue;
  204.                     }
  205.                     /* if whitespace follows txtch, then end-of-field */
  206.                     if (isspace(rrecs(rp)[col+1])) {
  207.                         /* skip to end of trailing whitespace */
  208.                         for (col++; col < rreclen(rp); col++) {
  209.                             if (!isspace(rrecs(rp)[col])) {
  210.                                 col--;
  211.                                 break;
  212.                             }
  213.                         }
  214.                         break;
  215.                     }
  216.                 }
  217.             }
  218.         } else {                  /* fldch != ' '; txtch != ' ' */
  219.             /* skip over characters that are not txtch */
  220.             for (col++; col < rreclen(rp); col++) {
  221.                 /* if txtch, see if end-of-field */
  222.                 if (ristxtch(rp, rrecs(rp)[col])) {
  223.                     /* don't end field within any nested quoted text */
  224.                     if (ristxtch(rp, '"')) {
  225.                         qstate = !qstate;
  226.                         if (!qstate) continue;
  227.                     }
  228.                     /* skip any whitespace */
  229.                     for (co=col+1; co < rreclen(rp); co++) {
  230.                         if (!isspace(rrecs(rp)[co])) break;
  231.                     }
  232.                     /* if fldch, then end-of-field */
  233.                     if (risfldch(rp, rrecs(rp)[co])) {
  234.                         col = co;
  235.                         break;
  236.                     }
  237.                 }
  238.             }
  239.         }
  240.     } else {                      /* else not quoted text */
  241.         if (risfldch(rp, ' ')) {  /* fldch == ' '; txtch == ' ' */
  242.             /* skip non-whitespace */
  243.             for (; col < rreclen(rp); col++) {
  244.                 if (isspace(rrecs(rp)[col])) break;
  245.             }
  246.             /* skip to end of trailing whitespace */
  247.             for (; col < rreclen(rp); col++) {
  248.                 if (!isspace(rrecs(rp)[col])) {
  249.                     col--;
  250.                     break;
  251.                 }
  252.             }
  253.         } else {                 /* fldch != ' '; txtch == ' ' */
  254.             /* skip over characters until fldch reached */
  255.             for (; col < rreclen(rp); col++) {
  256.                 if (risfldch(rp, rrecs(rp)[col])) break;
  257.             }
  258.         }
  259.     }
  260.     
  261.     /* get length of field */
  262.     if (rcol(rp) < rreclen(rp)) {
  263.         len = col - rcol(rp) + 1;
  264.     }
  265.     return len;
  266. }
  267.  
  268. /****************************************************************************/
  269. static int                   /* return error state (0=no error; EOF=error)  */
  270.     _rskipfld(               /* skip to the next field                      */
  271.         REC   *rp,           /* record pointer                              */
  272.         size_t len)          /* length of field if known, 0 if unknown      */
  273. /****************************************************************************/
  274. {
  275.     int err=0;          /* error state (0=no error; EOF=past end-of-record) */
  276.  
  277.     /* make sure first record is read from file */
  278.     if (!rrecno(rp) && !rgetrec(rp)) goto done;
  279.  
  280.     /* if length of field is unknown */
  281.     if (!len) {
  282.         /* determine length */
  283.         len=_rfldlen(rp);
  284.     }
  285.     
  286.     /* if field delimiter is whitespace */
  287.     if (risfldch(rp, ' ')) {
  288.         /* if not at end of record */
  289.         if (rcol(rp) < rreclen(rp)) {
  290.             /* move to next field */
  291.             rcol(rp) += max(len, 1);
  292.         } else {
  293.             /* cannot move beyond end of record */
  294.             err = EOF;
  295.         }
  296.     } else {
  297.         /* allow for empty field past last field delimiter */
  298.         if (rcol(rp) <= rreclen(rp)) {
  299.             /* move to next field */
  300.             rcol(rp) += max(len, 1);
  301.         } else {
  302.             /* cannot move beyond end of record */
  303.             err = EOF;
  304.         }
  305.     }
  306.     rfldno(rp)++;
  307.  
  308. done:
  309.     return err;
  310. }
  311.  
  312. /****************************************************************************/
  313. char *                       /* return pointer to field buffer (NULL=error) */
  314.     _rfldstr(                /* copy field from record to field buffer      */
  315.         REC   *rp,           /* record pointer                              */
  316.         size_t len)          /* length of field if known; 0 if unknown      */
  317. /****************************************************************************/
  318. {
  319.     char  *fldp=NULL;        /* pointer to field buffer (NULL=error) */
  320.     size_t fldlen=len;       /* computed length of field */
  321.  
  322.     /* make sure first record is read from file */
  323.     if (!rrecno(rp) && !rgetrec(rp)) goto done;
  324.  
  325.     /* if character delimited field, compute length */
  326.     if (!fldlen) {
  327.         fldlen=_rfldlen(rp);
  328.  
  329.     /* if column delimited field, avoid overflow */
  330.     } else if (rcol(rp) > rreclen(rp)) {
  331.         fldlen = 0;
  332.     }
  333.     
  334.     /* ensure field buffer has sufficient memory */
  335.     if (_rsetfldsiz(rp, fldlen)) goto done;
  336.  
  337.     /* copy field from record buffer to field buffer */
  338.     /* note: a missing field results in an empty string */
  339.     strncpy(rflds(rp), rrecs(rp)+rcol(rp), fldlen);
  340.     rflds(rp)[fldlen] = '\0';
  341.  
  342.     /* set up for next field */
  343.     _rskipfld(rp, max(fldlen, 1));
  344.  
  345.     /* if character delimited field, trim delimiters from field buffer */
  346.     if (!len) {
  347.         /* trim fldch */
  348.         scntrimends(rflds(rp), rfldch(rp), 1);
  349.  
  350.         /* if whitespace field delimiter, trim whitespace */
  351.         if (risfldch(rp, ' ')) {
  352.             strims(rflds(rp));
  353.         }
  354.         
  355.         /* if quoted text, trim txtch */
  356.         if (ristxtfld(rp)) {
  357.             strims(rflds(rp));
  358.             scntrims(rflds(rp), rtxtch(rp), 1);
  359.         }
  360.     }
  361.  
  362.     /* assign return pointer to field buffer */
  363.     fldp = rflds(rp);
  364.  
  365. done:
  366.     return fldp;
  367. }
  368.  
  369. /****************************************************************************/
  370. char *                       /* return ptr to rec buffer (NULL=err or eof)  */
  371.     rgetrec(                 /* read next line from file into record buffer */
  372.         REC *rp)             /* record pointer                              */
  373. /****************************************************************************/
  374. {
  375.     char *retp=NULL;         /* return pointer (NULL=error or eof) */
  376.     fpos_t bookmark;         /* file position before record is read */
  377.     size_t newsize;          /* if needed, new size of record buffer */
  378.  
  379.  
  380.     if (_risready(rp, R_READ)) {
  381.  
  382.         /* initially allocate memory for record buffer */
  383.         if (!rrecs(rp)) {
  384.             if (_rsetrecsiz(rp, RECBUFSIZE)) goto done;
  385.         }
  386.  
  387.         /* for each new record */
  388.         rfldno(rp) = 0;
  389.         rcol(rp) = 0;
  390.         rrecno(rp)++;
  391.         rreclen(rp) = 0;
  392.         *rrecs(rp) = '\0';
  393.  
  394.         /* if at end of file, skip reading from file */
  395.         if (reof(rp)) goto done;
  396.  
  397.         /* get current file position */
  398.         if (fgetpos(rfp(rp), &bookmark)) {
  399.           rseterr(NULL, errno);
  400.           goto done;
  401.         }
  402.  
  403.         /* loop until record buffer is large enough */
  404.         for (;;) {
  405.  
  406.           /* get next line from file into record buffer */
  407.           if (!fgets(rrecs(rp), rrecsiz(rp), rfp(rp))) {
  408.             /* set end-of-file indicator if no more records */
  409.             rflags(rp) |= _R_EOF;
  410.             goto done;
  411.           }
  412.  
  413.           /* if line longer than record buffer, determine size of buffer */
  414.           if (rrecs(rp)[strlen(rrecs(rp))-1] != '\n') {
  415.             for (newsize=rrecsiz(rp)+1; getc(rfp(rp)) != '\n'; newsize++) {
  416.               if (newsize == SIZE_T_MAX) {
  417.                 rseterr(rp, R_ENOGET);
  418.                 goto done;
  419.               }
  420.             }
  421.  
  422.             /* reset file position to bookmark */
  423.             if (fsetpos(rfp(rp), &bookmark)) {
  424.                rseterr(NULL, errno);
  425.                goto done;
  426.             }
  427.  
  428.             /* resize buffer */
  429.             if (_rsetrecsiz(rp, newsize)) goto done;
  430.             
  431.             /* reread line into resized record buffer */
  432.             continue;
  433.           }
  434.           break;
  435.         }
  436.  
  437.         /* trim end of record */
  438.         sctrimends(rrecs(rp), '\n');
  439.         rreclen(rp) = strlen(rrecs(rp));
  440.  
  441.         /* point retp to record buffer */
  442.         retp = rrecs(rp);
  443.     }
  444. done:
  445.     return retp;
  446. }
  447.  
  448. /****************************************************************************/
  449. void                         /* returns nothing                             */
  450.     rsetrecsiz(              /* set record buffer size                      */
  451.         REC   *rp,           /* record pointer                              */
  452.         size_t recsiz)       /* record buffer size                          */
  453. /****************************************************************************/
  454. {
  455.     if (_risready(rp, R_READ)) {
  456.         _rsetrecsiz(rp, recsiz);
  457.     }
  458. }
  459.  
  460. /****************************************************************************/
  461. void                         /* returns nothing                             */
  462.     rsetfldsiz(              /* set field buffer size                       */
  463.         REC   *rp,           /* record pointer                              */
  464.         size_t fldsiz)       /* field buffer size                           */
  465. /****************************************************************************/
  466. {
  467.     if (_risready(rp, R_READ)) {
  468.         _rsetfldsiz(rp, fldsiz);
  469.     }
  470. }
  471.  
  472. /****************************************************************************/
  473. void                         /* returns nothing                             */
  474.     rsetrecstr(              /* copy string to record buffer; clear errors  */
  475.         REC  *rp,            /* record pointer                              */
  476.   const char *s)             /* pointer to string                           */
  477. /****************************************************************************/
  478. {
  479.     size_t recsiz;           /* required record buffer size */
  480.  
  481.     if (risvalid(rp)) {
  482.         if (!_rmoderror(rp, R_READ)) {
  483.             if (s) {
  484.                 /* ensure record buffer is large enough for string */
  485.                 recsiz = strlen(s);
  486.                 if (recsiz > rrecsiz(rp)) {
  487.                     if (_rsetrecsiz(rp, recsiz)) goto done;
  488.                 }
  489.             
  490.                 /* copy string to record buffer */
  491.                 if (s != rrecs(rp)) strcpy(rrecs(rp), s);
  492.     
  493.                 /* prepare stream to read */
  494.                 rclearerr(rp);
  495.                 rfldno(rp) = 0;
  496.                 rcol(rp) = 0;
  497.                 rreclen(rp) = recsiz;
  498.                 if (!rrecno(rp)) rrecno(rp)++;
  499.         
  500.             } else {
  501.                 rseterr(rp, R_EINVAL);
  502.             }
  503.         }
  504.     } else {
  505.         rseterr(NULL, EINVAL);
  506.     }
  507. done:
  508.     return;
  509. }
  510.  
  511. /****************************************************************************/
  512. void                         /* returns nothing                             */
  513.     rsetfldstr(              /* copy string into field buffer; clear errors */
  514.         REC  *rp,            /* record pointer                              */
  515.   const char *s)             /* pointer to string                           */
  516. /****************************************************************************/
  517. {
  518.     size_t fldsiz;           /* required field buffer size */
  519.  
  520.     if (risvalid(rp)) {
  521.         if (!_rmoderror(rp, R_READ)) {
  522.             if (s) {
  523.                 /* ensure field buffer is large enough for string */
  524.                 fldsiz = strlen(s);
  525.                 if (fldsiz > rfldsiz(rp)) {
  526.                     if (_rsetfldsiz(rp, fldsiz)) goto done;
  527.                 }
  528.             
  529.                 /* copy string to field buffer */
  530.                 strcpy(rflds(rp), s);
  531.  
  532.                 /* clear away any errors */
  533.                 rclearerr(rp);
  534.  
  535.             } else {
  536.                 rseterr(rp, R_EINVAL);
  537.             }
  538.         }
  539.     } else {
  540.         rseterr(NULL, EINVAL);
  541.     }
  542. done:
  543.     return;
  544. }
  545.  
  546. /****************************************************************************/
  547. void                         /* returns nothing                             */
  548.     rskipnfld(               /* skip over next number of fields             */
  549.         REC     *rp,         /* record pointer                              */
  550.         unsigned num)        /* number of fields to skip over               */
  551. /****************************************************************************/
  552. {
  553.     unsigned count=0;        /* count number of fields */
  554.     unsigned curfldno;       /* current field number */
  555.  
  556.     if (_risready(rp, R_READ)) {
  557.         curfldno = rfldno(rp);
  558.         /* count number of fields to skip */
  559.         for (; count < num; count++) {
  560.             /* but don't skip past end of record */
  561.             if (_rskipfld(rp, 0)) break;
  562.         }
  563.         rfldno(rp) = curfldno + num;
  564.     }
  565. }
  566.  
  567. /****************************************************************************/
  568. void                         /* returns nothing                             */
  569.     rgotofld(                /* go to field number                          */
  570.         REC     *rp,         /* record pointer                              */
  571.         unsigned num)        /* requested field number                      */
  572. /****************************************************************************/
  573. {
  574.     if (_risready(rp, R_READ)) {
  575.         if (num-- > 0) {     /* decrement num to destination rfldno */
  576.             /* if current field beyond requested field */
  577.             if (rfldno(rp) > num ) {
  578.                 /* start from first field */
  579.                 rfldno(rp) = 0;
  580.                 rcol(rp) = 0;
  581.             }
  582.             /* skip fields until requested field reached */
  583.             rskipnfld(rp, num - rfldno(rp));
  584.         } else {
  585.             rseterr(rp, R_EINVAL);
  586.         }
  587.     }
  588. }
  589.  
  590. /****************************************************************************/
  591. void                         /* returns nothing                             */
  592.     _rgetfldpos(             /* get field position                          */
  593.         REC    *rp,          /* record pointer                              */
  594.         rpos_t *pos)         /* pointer to field position                   */
  595. /****************************************************************************/
  596. {
  597.     if (_risready(rp, R_READ)) {
  598.         rfd(pos) = rfd(rp);
  599.         rfldno(pos) = rfldno(rp);
  600.         rcol(pos) = rcol(rp);
  601.         rrecno(pos) = rrecno(rp);
  602.     }
  603. }
  604.  
  605. /****************************************************************************/
  606. void                         /* returns nothing                             */
  607.     _rsetfldpos(             /* set field position                          */
  608.         REC    *rp,          /* record pointer                              */
  609.   const rpos_t *pos)         /* pointer to field position                   */
  610. /****************************************************************************/
  611. {
  612.     if (_risready(rp, R_READ)) {
  613.         if (rfd(pos)==rfd(rp) && rrecno(pos)==rrecno(rp)) {
  614.             rfldno(rp) = rfldno(pos);
  615.             rcol(rp) = rcol(pos);
  616.         } else {
  617.             rseterr(rp, R_EINVAL);
  618.         }
  619.     }
  620. }
  621.  
  622. /****************************************************************************/
  623. unsigned                     /* return number of fields in current record   */
  624.     rnumfld(                 /* number of fields in current record          */
  625.         REC *rp)             /* record pointer                              */
  626. /****************************************************************************/
  627. {
  628.     unsigned num=0;          /* return number of fields in current record */
  629.     rpos_t pos;              /* field position */
  630.  
  631.     if (_risready(rp, R_READ)) {
  632.         /* store current field information */
  633.         rgetfldpos(rp, pos);
  634.  
  635.         /* get number of fields from beginning of record */
  636.         rfldno(rp) = 0;
  637.         rcol(rp) = 0;
  638.  
  639.         /* count number of fields skipped */
  640.         for (; num < UINT_MAX; num++) {
  641.             /* but don't skip past end of record */
  642.             if (_rskipfld(rp, 0)) break;
  643.         }
  644.  
  645.         /* restore field information */
  646.         rsetfldpos(rp, pos);
  647.     }
  648.     return num;
  649. }
  650.  
  651. /****************************************************************************/
  652. unsigned long                /* return unsigned long                        */
  653.     str2ul(                  /* convert string to unsigned long             */
  654.         const char  *nptr,   /* pointer to string to convert                */
  655.               char **endptr, /* pointer to conversion leftover string       */
  656.               int    base)   /* base (radix) of number                      */
  657. /****************************************************************************/
  658. /* note: unlike strtoul, str2ul tests for a negative number */
  659. {
  660.     unsigned long ul=0UL;    /* result to return */
  661.     const char   *np=nptr;   /* pointer to string */
  662.  
  663.     /* skip over white space */
  664.     while (isspace(*np)) np++;
  665.     
  666.     /* if first non-white space is a minus sign */
  667.     if (*np == '-') {
  668.         
  669.         /* position endptr at the minus sign */
  670.         if (endptr) *endptr = (char *) np;
  671.         
  672.         /* set ERANGE error */
  673.         errno = ERANGE;
  674.  
  675.     } else {
  676.  
  677.         ul = strtoul(nptr, endptr, base);
  678.  
  679.     } 
  680.     return ul;
  681. }
  682.